/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"

#if CC_ContextSave  // Conditional expansion of this file

#include "ContextSave_fp.h"
#include "Marshal.h"
#include "Context_spt_fp.h"

/*
This command saves a session context, object context, or sequence object context outside the TPM.
No authorization sessions of any type are allowed with this command and tag is required to be
TPM_ST_NO_SESSIONS.
NOTE This preclusion avoids complex issues of dealing with the same session in handle and in the session area.
While it might be possible to provide specificity, it would add unnecessary complexity to the TPM and, because 
this capability would provide no application benefit, use of authorization sessions for audit or encryption is prohibited.
The TPM shall encrypt and integrity protect the TPM2B_CONTEXT_SENSITIVE context as described in the "Context Protections" 
clause in TPM 2.0 Part 1.
See the “Context Data” clause in TPM 2.0 Part 2 for a description of the context structure in the response
此命令在TPM外部保存会话上下文、对象上下文或序列对象上下文。
此命令不允许使用任何类型的授权会话，并且需要标记
TPM_ST_NO_SESSIONS。
注：这种排除避免了在句柄和会话区域中处理同一会话的复杂问题。虽然可以提供特定性，但这会给TPM增加不必要的复杂性，
而且由于这种功能不会给应用程序带来任何好处，因此禁止使用授权会话进行审计或加密。
TPM应加密TPM 2.0第1部分“上下文保护”条款中所述的TPM2B_CONTEXT_SENSITIVE上下文并对其进行完整性保护。
有关响应中上下文结构的描述，请参阅TPM 2.0第2部分中的“上下文数据”条款
*/
/*(See part 3 specification)
 Save context
*/
//  Return Type: TPM_RC
//      TPM_RC_CONTEXT_GAP          a contextID could not be assigned for a session
//                                  context save
//      TPM_RC_TOO_MANY_CONTEXTS    no more contexts can be saved as the counter has
//                                  maxed out
TPM_RC
TPM2_ContextSave(
    ContextSave_In      *in,            // IN: input parameter list
    ContextSave_Out     *out            // OUT: output parameter list
    )
{
    TPM_RC          result = TPM_RC_SUCCESS;
    UINT16          fingerprintSize;    // The size of fingerprint in context
    // blob.
    UINT64          contextID = 0;      // session context ID
    TPM2B_SYM_KEY   symKey;
    TPM2B_IV        iv;

    TPM2B_DIGEST    integrity;
    UINT16          integritySize;
    BYTE            *buffer;

    // This command may cause the orderlyState to be cleared due to
    // the update of state reset data. If the state is orderly and
    // cannot be changed, exit early.
    RETURN_IF_ORDERLY;
    
// Internal Data Update

// This implementation does not do things in quite the same way as described in
// Part 2 of the specification. In Part 2, it indicates that the 
// TPMS_CONTEXT_DATA contains two TPM2B values. That is not how this is 
// implemented. Rather, the size field of the TPM2B_CONTEXT_DATA is used to 
// determine the amount of data in the encrypted data. That part is not 
// independently sized. This makes the actual size 2 bytes smaller than 
// calculated using Part 2. Since this is opaque to the caller, it is not 
// necessary to fix. The actual size is returned by TPM2_GetCapabilties().

    // Initialize output handle.  At the end of command action, the output
    // handle of an object will be replaced, while the output handle
    // for a session will be the same as input
    out->context.savedHandle = in->saveHandle;

    // Get the size of fingerprint in context blob.  The sequence value in
    // TPMS_CONTEXT structure is used as the fingerprint
    fingerprintSize = sizeof(out->context.sequence);

    // Compute the integrity size at the beginning of context blob
    integritySize = sizeof(integrity.t.size)
        + CryptHashGetDigestSize(CONTEXT_INTEGRITY_HASH_ALG);

// Perform object or session specific context save
    switch(HandleGetType(in->saveHandle))
    {
        case TPM_HT_TRANSIENT:
        {
            OBJECT              *object = HandleToObject(in->saveHandle);
            ANY_OBJECT_BUFFER   *outObject;
            UINT16               objectSize = ObjectIsSequence(object)
                ? sizeof(HASH_OBJECT) : sizeof(OBJECT);

            outObject = (ANY_OBJECT_BUFFER *)(out->context.contextBlob.t.buffer
                                              + integritySize + fingerprintSize);

            // Set size of the context data.  The contents of context blob is vendor
            // defined.  In this implementation, the size is size of integrity
            // plus fingerprint plus the whole internal OBJECT structure
            out->context.contextBlob.t.size = integritySize +
                fingerprintSize + objectSize;
#if ALG_RSA
            // For an RSA key, make sure that the key has had the private exponent
            // computed before saving.
            if(object->publicArea.type == TPM_ALG_RSA &&
               !(object->attributes.publicOnly))
                CryptRsaLoadPrivateExponent(&object->publicArea, &object->sensitive);
#endif
            // Make sure things fit
            pAssert(out->context.contextBlob.t.size
                    <= sizeof(out->context.contextBlob.t.buffer));
            // Copy the whole internal OBJECT structure to context blob
            MemoryCopy(outObject, object, objectSize);

            // Increment object context ID
            gr.objectContextID++;
            // If object context ID overflows, TPM should be put in failure mode
            if(gr.objectContextID == 0)
                FAIL(FATAL_ERROR_INTERNAL);

            // Fill in other return values for an object.
            out->context.sequence = gr.objectContextID;
            // For regular object, savedHandle is 0x80000000.  For sequence object,
            // savedHandle is 0x80000001.  For object with stClear, savedHandle
            // is 0x80000002
            if(ObjectIsSequence(object))
            {
                out->context.savedHandle = 0x80000001;
                SequenceDataExport((HASH_OBJECT *)object,
                                   (HASH_OBJECT_BUFFER *)outObject);
            }
            else
                out->context.savedHandle = (object->attributes.stClear == SET)
                ? 0x80000002 : 0x80000000;
// Get object hierarchy
            out->context.hierarchy = ObjectGetHierarchy(object);

            break;
        }
        case TPM_HT_HMAC_SESSION:
        case TPM_HT_POLICY_SESSION:
        {
            SESSION         *session = SessionGet(in->saveHandle);

            // Set size of the context data.  The contents of context blob is vendor
            // defined.  In this implementation, the size of context blob is the
            // size of a internal session structure plus the size of
            // fingerprint plus the size of integrity
            out->context.contextBlob.t.size = integritySize +
                fingerprintSize + sizeof(*session);

            // Make sure things fit
            pAssert(out->context.contextBlob.t.size
                    < sizeof(out->context.contextBlob.t.buffer));

            // Copy the whole internal SESSION structure to context blob.
            // Save space for fingerprint at the beginning of the buffer
            // This is done before anything else so that the actual context
            // can be reclaimed after this call
            pAssert(sizeof(*session) <= sizeof(out->context.contextBlob.t.buffer)
                    - integritySize - fingerprintSize);
            MemoryCopy(out->context.contextBlob.t.buffer + integritySize 
                       + fingerprintSize, session, sizeof(*session));
           // Fill in the other return parameters for a session
           // Get a context ID and set the session tracking values appropriately
           // TPM_RC_CONTEXT_GAP is a possible error.
           // SessionContextSave() will flush the in-memory context
           // so no additional errors may occur after this call.
            result = SessionContextSave(out->context.savedHandle, &contextID);
            if(result != TPM_RC_SUCCESS)
                return result;
            // sequence number is the current session contextID
            out->context.sequence = contextID;

            // use TPM_RH_NULL as hierarchy for session context
            out->context.hierarchy = TPM_RH_NULL;

            break;
        }
        default:
            // SaveContext may only take an object handle or a session handle.
            // All the other handle type should be filtered out at unmarshal
            FAIL(FATAL_ERROR_INTERNAL);
            break;
    }

    // Save fingerprint at the beginning of encrypted area of context blob.
    // Reserve the integrity space
    pAssert(sizeof(out->context.sequence) <=
            sizeof(out->context.contextBlob.t.buffer) - integritySize);
    MemoryCopy(out->context.contextBlob.t.buffer + integritySize,
               &out->context.sequence, sizeof(out->context.sequence));

    // Compute context encryption key
    ComputeContextProtectionKey(&out->context, &symKey, &iv);

    // Encrypt context blob
    CryptSymmetricEncrypt(out->context.contextBlob.t.buffer + integritySize,
                          CONTEXT_ENCRYPT_ALG, CONTEXT_ENCRYPT_KEY_BITS,
                          symKey.t.buffer, &iv, TPM_ALG_CFB,
                          out->context.contextBlob.t.size - integritySize,
                          out->context.contextBlob.t.buffer + integritySize);

    // Compute integrity hash for the object
    // In this implementation, the same routine is used for both sessions
    // and objects.
    ComputeContextIntegrity(&out->context, &integrity);

    // add integrity at the beginning of context blob
    buffer = out->context.contextBlob.t.buffer;
    TPM2B_DIGEST_Marshal(&integrity, &buffer, NULL);

    // orderly state should be cleared because of the update of state reset and
    // state clear data
    g_clearOrderly = TRUE;

    return result;
}

#endif // CC_ContextSave